/*
 *    Copyright © OpenAtom Foundation.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package com.inspur.edp.bef.engine.core.dtm;

import com.inspur.edp.bef.bizentity.GspBizEntityObject;
import com.inspur.edp.bef.core.determination.builtinimpls.predicate.ExecutePredicate;
import com.inspur.edp.bef.core.determination.builtinimpls.predicate.ExecutePredicateFactory;
import com.inspur.edp.bef.engine.BefEngineException;
import com.inspur.edp.cef.designtime.api.collection.BaseList;
import com.inspur.edp.cef.designtime.api.collection.DtmElementCollection;
import com.inspur.edp.cef.designtime.api.operation.ChildDtmTriggerInfo;
import com.inspur.edp.cef.designtime.api.operation.CommonDetermination;
import com.inspur.edp.cef.designtime.api.operation.ExecutingDataStatus;
import com.inspur.edp.cef.entity.changeset.IChangeDetail;
import com.inspur.edp.das.commonmodel.IGspCommonElement;
import com.inspur.edp.das.commonmodel.IGspCommonObject;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

public class DtmUtil {

  public static ExecutePredicate buildCanExecute(GspBizEntityObject node, CommonDetermination op) {
    if (op.getRequestChildElements() != null && !op.getRequestChildElements().isEmpty()) {
      return new CompatiblePredicate(node, op);
    } else {
      List<ExecutePredicate> predicates = new ArrayList<>();

      buildCanExecuteByChangeTypes(node, op, predicates);
      buildCanExecuteByChildChange(node, op, predicates);

      return ExecutePredicateFactory
          .getAggregatePredicate(predicates.toArray(new ExecutePredicate[0]));
    }
  }

  private static void buildCanExecuteByChildChange(GspBizEntityObject node, CommonDetermination op,
      List<ExecutePredicate> predicates) {
    if (op.getChildTriggerInfo() == null) {
      return;
    }
    for (Entry<String, ChildDtmTriggerInfo> entry : op.getChildTriggerInfo().entrySet()) {
      String childObjId = entry.getKey();
      IGspCommonObject childObj = node.getChildObjectById(childObjId);
      if(childObj == null) {
        throw new BefEngineException(
            "事件[" + op.getCode() + "]上定义的子节点触发条件无效,可以尝试重新设置触发条件.(id:" + childObjId + ")");
      }
      ChildDtmTriggerInfo childDtmTriggerInfo = entry.getValue();
      if (childDtmTriggerInfo.getGetExecutingDataStatus() != null) {
        for (ExecutingDataStatus ds : childDtmTriggerInfo.getGetExecutingDataStatus()) {
          switch (ds) {
            case added:
              predicates.add(ExecutePredicateFactory.getChildAddPredicate(childObj.getCode()));
              break;
            case Deleted:
              predicates.add(ExecutePredicateFactory.getChildDeletePredicate(childObj.getCode()));
              break;
            case Modify:
              List<String> elements = convert2LabelIdList(childObj,
                  childDtmTriggerInfo.getRequestChildElements());
              ;
              predicates
                  .add(ExecutePredicateFactory
                      .getChildModifyPredicate(childObj.getCode(), elements));
              break;
          }

        }
      }
    }
  }

  private static void buildCanExecuteByChangeTypes(GspBizEntityObject node,
      CommonDetermination op, List<ExecutePredicate> predicates) {
    if (op.getGetExecutingDataStatus() == null) {
      return;
    }
    for (ExecutingDataStatus ds : op.getGetExecutingDataStatus()) {
      switch (ds) {
        case added:
          predicates.add(ExecutePredicateFactory.getAddPredicate());
          break;
        case Deleted:
          predicates.add(ExecutePredicateFactory.getDeletePredicate());
          break;
        case Modify:
          List<String> elements = convert2LabelIdList(node, op.getRequestElements());
          predicates.add(ExecutePredicateFactory.getModifyPredicate(elements));
          break;
      }
    }
  }

  private static List<String> convert2LabelIdList(IGspCommonObject node, BaseList<String> op) {
    if (op == null || op.isEmpty()) {
      return null;
    }
    List<String> elements = new ArrayList<>(op.size());
    for (String item : op) {
      IGspCommonElement element = node.findElement(item);
      elements.add(element.getLabelID());
    }
    return elements;
  }

  //兼容老版be设计器触发字段选子表
  private static class CompatiblePredicate implements ExecutePredicate {

    private final CommonDetermination determination;
    private final GspBizEntityObject gspBizEntityObject;

    public CompatiblePredicate(GspBizEntityObject node, CommonDetermination determination) {
      this.determination = determination;
      this.gspBizEntityObject = node;
    }

    @Override
    public boolean test(IChangeDetail change) {
      ExecutingDataStatus temp = ExecutingDataStatus.None;
      switch (change.getChangeType()) {
        case Added:
          temp = ExecutingDataStatus.added;
          break;
        case Modify:
          temp = ExecutingDataStatus.Modify;
          break;
        case Deleted:
          temp = ExecutingDataStatus.Deleted;
          break;
      }
      if (determination.getGetExecutingDataStatus().size() > 0) {
        boolean triggerVaild = false;
        for (ExecutingDataStatus triggerTimePointType : determination.getGetExecutingDataStatus()) {
          if (temp == triggerTimePointType) {
            triggerVaild = true;
          }
        }
        if (!triggerVaild) {
          return false;
        }
      }
      //触发字段？
      if (this.determination.getRequestElements().getCount() > 0) {
        List<String> list = convert2LabelIdList(gspBizEntityObject,
            determination.getRequestElements());
        if (com.inspur.edp.cef.entity.changeset.Util.containsAny(change, list)) {
          return true;
        }
        return false;
      }

      if (this.determination.getRequestChildElements().size() > 0) {
        for (Map.Entry<String, DtmElementCollection> pair : this.determination
            .getRequestChildElements().entrySet()) {
          IGspCommonObject childNode = this.gspBizEntityObject.getChildObjectById(pair.getKey());
          List<String> fields = convert2LabelIdList(childNode, pair.getValue());
          if (com.inspur.edp.cef.entity.changeset.Util.containsChildAny(
              change, childNode.getCode(), fields.toArray(new String[fields.size()]))) {
            return true;
          }
        }
        return false;
      }
      return true;
    }
  }
}
